package com.eedi.framework.permission.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.eedi.framework.permission.controller.org.vo.role.*;
import com.google.common.annotations.VisibleForTesting;
import com.eedi.framework.common.enums.CommonStatusEnum;
import com.eedi.framework.common.enums.UserTypeEnum;
import com.eedi.framework.common.exception.util.ServiceExceptionUtil;
import com.eedi.framework.common.pojo.PageResult;
import com.eedi.framework.common.util.collection.CollectionUtils;
import com.eedi.framework.common.util.object.BeanUtils;
import com.eedi.framework.permission.controller.org.vo.role.*;
import com.eedi.framework.permission.convert.OrgRoleConvert;
import com.eedi.framework.permission.dal.OrgPermissionCacheKeyConstants;
import com.eedi.framework.permission.dal.dataobject.OrgRoleDO;
import com.eedi.framework.permission.dal.mapper.OrgRoleMapper;
import com.eedi.framework.permission.dal.mapper.OrgUserRoleMapper;
import com.eedi.framework.permission.enums.OrgDataScopeEnum;
import com.eedi.framework.permission.enums.OrgPermissionErrorCodeConstants;
import com.eedi.framework.permission.enums.OrgRoleCodeEnum;
import com.eedi.framework.permission.enums.OrgRoleTypeEnum;
import com.eedi.framework.template.dal.dataobject.SysTemplateRoleDO;
import com.eedi.framework.template.service.SysTemplateRoleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

import static com.eedi.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.eedi.framework.common.util.collection.CollectionUtils.convertMap;
import static com.eedi.framework.permission.enums.OrgPermissionErrorCodeConstants.ORG_ROLE_NAME_DUPLICATE;
import static com.eedi.framework.permission.enums.OrgPermissionErrorCodeConstants.ORG_TEMPLATE_ROLE_IS_NULL;

/**
 * 角色 Service 实现类
 *
 * @author 永聚长青源码
 */
@Service
@Slf4j
public class OrgRoleServiceImpl implements OrgRoleService {
    @Resource
    private OrgRoleMapper orgRoleMapper;
    @Resource
    private OrgUserRoleMapper orgUserRoleMapper;
    @Resource
    private SysTemplateRoleService sysTemplateRoleService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String createRole(OrgRoleSaveReq createReqVO) {
        // 校验角色
        validateRoleDuplicate(createReqVO, null);
        // 插入到数据库
        OrgRoleDO role = BeanUtils.toBean(createReqVO, OrgRoleDO.class);
        role.setOrgRoleType(OrgRoleTypeEnum.CUSTOM);
        role.setOrgRoleStatus(CommonStatusEnum.ENABLE);
        role.setOrgRoleCode(OrgRoleCodeEnum.DEFAULT.getCode());
        role.setOrgRoleSort(0);
        // 默认可查看所有数据。原因是，可能一些项目不需要项目权限
        role.setOrgRoleDataScope(OrgDataScopeEnum.ALL);
        orgRoleMapper.insert(role);
        return role.getOrgRoleId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String createRole(SysOrgRoleCreateReq createReq) {
        // 1. 该 name 名字被其它角色所使用
        OrgRoleDO role = null;
        if(!Objects.isNull(createReq.getOrgInfoId())){
            role = orgRoleMapper.selectByNameAndOrgInfoId(createReq.getOrgInfoId(),createReq.getOrgRoleName(), UserTypeEnum.MEMBER_OPS);
        }else {
             role = orgRoleMapper.selectByName(createReq.getOrgRoleName(), UserTypeEnum.MEMBER_OPS);
        }
        if (role != null ) {
            throw ServiceExceptionUtil.exception(ORG_ROLE_NAME_DUPLICATE, createReq.getOrgRoleName());
        }
        OrgRoleDO insertDO = BeanUtils.toBean(createReq, OrgRoleDO.class);
        insertDO.setOrgRoleDataScope(OrgDataScopeEnum.ALL);
        orgRoleMapper.insert(insertDO);
        return insertDO.getOrgRoleId();
    }

    @Override
    @CacheEvict(value = OrgPermissionCacheKeyConstants.ROLE, key = "#updateReqVO.orgRoleId")
    public void updateRole(OrgRoleUpdateReq updateReqVO) {
        // 校验是否可以更新
        validateRoleForUpdate(updateReqVO.getOrgRoleId());
        // 校验角色的唯一字段是否重复
        validateRoleDuplicateForUpdate(updateReqVO, updateReqVO.getOrgRoleId());

        // 更新到数据库
        OrgRoleDO updateObj = BeanUtils.toBean(updateReqVO, OrgRoleDO.class);
        orgRoleMapper.updateById(updateObj);
    }

    @Override
    @CacheEvict(value = OrgPermissionCacheKeyConstants.ROLE, key = "#roleId")
    public void updateRoleStatus(String roleId, CommonStatusEnum status) {
        // 校验是否可以更新
        validateRoleForUpdate(roleId);

        // 更新状态
        OrgRoleDO updateObj = new OrgRoleDO().setOrgRoleId(roleId).setOrgRoleStatus(status);
        orgRoleMapper.updateById(updateObj);
    }

    @Override
    @CacheEvict(value = OrgPermissionCacheKeyConstants.ROLE, key = "#roleId")
    public void updateRoleDataScope(String roleId, OrgDataScopeEnum dataScope, Set<String> dataScopeDeptIds) {
        // 校验是否可以更新
        validateRoleForUpdate(roleId);

        // 更新数据范围
        OrgRoleDO updateObject = new OrgRoleDO();
        updateObject.setOrgRoleId(roleId);
        updateObject.setOrgRoleDataScope(dataScope);
        updateObject.setOrgRoleDataScopeDeptIds(dataScopeDeptIds);
        orgRoleMapper.updateById(updateObject);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = OrgPermissionCacheKeyConstants.ROLE, key = "#roleId")
    public void deleteRole(String roleId) {
        // 校验是否可以更新
        validateRoleForUpdate(roleId);
        // 标记删除
        orgRoleMapper.deleteById(roleId);
    }

    /**
     * 校验角色的唯一字段是否重复
     * 1. 是否存在相同名字的角色
     * 2. 是否存在相同编码的角色
     * @param orgRoleSaveReq
     * @param roleId
     */
    @VisibleForTesting
    void validateRoleDuplicate(OrgRoleSaveReq orgRoleSaveReq, String roleId){
        // 1. 该 name 名字被其它角色所使用
        OrgRoleDO role = null;
        if(!Objects.isNull(orgRoleSaveReq.getOrgInfoId())){
            role = orgRoleMapper.selectByNameAndOrgInfoId(orgRoleSaveReq.getOrgInfoId(),orgRoleSaveReq.getOrgRoleName(), UserTypeEnum.MEMBER_OPS);
        }else {
            role = orgRoleMapper.selectByName(orgRoleSaveReq.getOrgRoleName(), UserTypeEnum.MEMBER_OPS);
        }
        if (role != null && !role.getOrgRoleId().equals(roleId)) {
            throw ServiceExceptionUtil.exception(ORG_ROLE_NAME_DUPLICATE, orgRoleSaveReq.getOrgRoleName());
        }
    }
    /**
     * 校验角色的唯一字段是否重复
     *
     * 1. 是否存在相同名字的角色
     * 2. 是否存在相同编码的角色
     * @param orgRoleUpdateReq
     * @param roleId
     */
    @VisibleForTesting
    void validateRoleDuplicateForUpdate(OrgRoleUpdateReq orgRoleUpdateReq, String roleId){
        // 1. 该 name 名字被其它角色所使用
        OrgRoleDO role = orgRoleMapper.selectByName(orgRoleUpdateReq.getOrgRoleName(), UserTypeEnum.MEMBER_OPS);
        if (role != null && !role.getOrgRoleId().equals(roleId)) {
            throw exception(ORG_ROLE_NAME_DUPLICATE, orgRoleUpdateReq.getOrgRoleName());
        }

    }

    /**
     * 校验角色是否可以被更新
     *
     * @param roleId 角色编号
     */
    @VisibleForTesting
    void validateRoleForUpdate(String roleId) {
        OrgRoleDO orgRoleDO = orgRoleMapper.selectById(roleId);
        if (orgRoleDO == null) {
            throw ServiceExceptionUtil.exception(OrgPermissionErrorCodeConstants.ORG_ROLE_NOT_EXISTS);
        }
        // 内置角色，不允许删除
        if (OrgRoleTypeEnum.SYSTEM.equals(orgRoleDO.getOrgRoleType())) {
            throw ServiceExceptionUtil.exception(OrgPermissionErrorCodeConstants.ORG_ROLE_CAN_NOT_UPDATE_ORGTEM_TYPE_ROLE);
        }
    }

    @Override
    public OrgRoleDO getRole(String roleId) {
        return orgRoleMapper.selectById(roleId);
    }

    @Override
    @Cacheable(value = OrgPermissionCacheKeyConstants.ROLE, key = "#roleId",
            unless = "#result == null")
    public OrgRoleDO getRoleFromCache(String roleId) {
        return orgRoleMapper.selectById(roleId);
    }


    @Override
    public List<OrgRoleDO> getRoleListByStatus(Collection<CommonStatusEnum> statuses) {
        return orgRoleMapper.selectListByStatus(statuses);
    }

    @Override
    public List<OrgRoleDO> getRoleList() {
        return orgRoleMapper.selectList();
    }

    @Override
    public List<OrgRoleDO> getRoleList(Collection<String> roleIds) {
        if (CollectionUtil.isEmpty(roleIds)) {
            return Collections.emptyList();
        }
        return orgRoleMapper.selectBatchIds(roleIds);
    }
    @Override
    public List<OrgRoleSimpleResp> getRoleSimpleList(Collection<String> orgRoleIds) {
        return orgRoleMapper.selectByRoleIds(orgRoleIds);
    }

    @Override
    public List<OrgRoleDO> getRoleListFromCache(Collection<String> roleIds) {
        if (CollectionUtil.isEmpty(roleIds)) {
            return Collections.emptyList();
        }
        // 这里采用 for 循环从缓存中获取，主要考虑 Spring CacheManager 无法批量操作的问题
        OrgRoleServiceImpl self = getSelf();
        return CollectionUtils.convertList(roleIds, self::getRoleFromCache);
    }

    @Override
    public PageResult<OrgRoleDO> getRolePage(OrgRolePageReq reqVO) {
        return orgRoleMapper.selectPage(reqVO);
    }

    @Override
    public boolean hasAnySuperAdmin(Collection<String> roleIds) {
        if (CollectionUtil.isEmpty(roleIds)) {
            return false;
        }
        OrgRoleServiceImpl self = getSelf();
        return roleIds.stream().anyMatch(roleId -> {
            OrgRoleDO role = self.getRoleFromCache(roleId);
            return role != null && (OrgRoleCodeEnum.isImplAdmin(role.getOrgRoleCode())
                    || OrgRoleCodeEnum.isSysAdmin(role.getOrgRoleCode())
                    || OrgRoleCodeEnum.isOrgAdmin(role.getOrgRoleCode()));
        });
    }

    @Override
    public void validateRoleList(Collection<String> roleIds) {
        if (CollUtil.isEmpty(roleIds)) {
            return;
        }
        // 获得角色信息
        List<OrgRoleDO> roles = orgRoleMapper.selectBatchIds(roleIds);
        Map<String, OrgRoleDO> roleMap = convertMap(roles, OrgRoleDO::getOrgRoleId);
        // 校验
        roleIds.forEach(roleId -> {
            OrgRoleDO role = roleMap.get(roleId);
            if (role == null) {
                throw ServiceExceptionUtil.exception(OrgPermissionErrorCodeConstants.ORG_ROLE_NOT_EXISTS);
            }
            if (!CommonStatusEnum.ENABLE.equals(role.getOrgRoleStatus())) {
                throw ServiceExceptionUtil.exception(OrgPermissionErrorCodeConstants.ORG_ROLE_IS_DISABLE, role.getOrgRoleName());
            }
        });
    }
    @Override
    public Long countAdmin() {
        return orgRoleMapper.countRoleCode(OrgRoleCodeEnum.ORG_ADMIN.getCode());
    }

    @Override
    public List<String> getOrgUserIdByDefaultOrgRoleCode() {
        List<OrgRoleSimpleResp> orgRoleSimples = orgRoleMapper.selectSysRoleCode(OrgRoleCodeEnum.DEFAULT);
        List<String> orgRoleIds = orgRoleSimples.stream().map(OrgRoleSimpleResp::getOrgRoleId).toList();
        if (CollUtil.isEmpty(orgRoleIds)){
            return List.of();
        }
        return orgUserRoleMapper.getByOrgRoleId(orgRoleIds);
    }

    @Override
    public Map<String, String> presupposeRole(String orgInfoId) {
        List<SysTemplateRoleDO> roleList = sysTemplateRoleService.getRoleList();
        List<OrgRoleResp> roleResps = OrgRoleConvert.INSTANCE.convert(roleList);
        Map<String,String> resMap=new HashMap<>();
        if (CollUtil.isEmpty(roleResps)){
            throw exception(ORG_TEMPLATE_ROLE_IS_NULL);
        }
        for (OrgRoleResp roleResp:roleResps){
            OrgRoleDO orgRoleDO = OrgRoleConvert.INSTANCE.convert(roleResp);
            orgRoleDO.setOrgRoleId(null);
            orgRoleDO.setOrgInfoId(orgInfoId);
            orgRoleMapper.insert(orgRoleDO);
            resMap.put(roleResp.getOrgRoleId(),orgRoleDO.getOrgRoleId());
        }
        return resMap;
    }

    /**
     * 获得自身的代理对象，解决 AOP 生效问题
     *
     * @return 自己
     */
    private OrgRoleServiceImpl getSelf() {
        return SpringUtil.getBean(getClass());
    }

}
